package com.baijiayun.liveuibase.widgets.popupwindow;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;

import androidx.annotation.CallSuper;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;

import com.baijiayun.liveuibase.R;
import com.baijiayun.liveuibase.utils.DisplayUtils;
import com.baijiayun.liveuibase.utils.ShadowUtil;
import com.baijiayun.liveuibase.utils.ThemeDataUtil;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class BaseAutoArrangePopupWindow extends PopupWindow implements LifecycleObserver {
    // 子类使用，自定义的 item 通用尺寸
    protected final static int COMMON_CUSTOM_ITEM_SIZE = 32;
    // colors 通用尺寸
    protected final static int COMMON_CUSTOM_COLORS_ITEM_SIZE = 24;

    protected final static int CENTER_ZERO = 0;
    public final static int LEFT_TOP = 1;
    public final static int RIGHT_TOP = 2;
    public final static int LEFT_BOTTOM = -1;
    public final static int RIGHT_BOTTOM = -2;

    protected final static int HORIZONTAL = 1;
    protected final static int VERTICAL = 2;

    protected final static int SINGLE = 1;
    protected final static int MULTIPLE = 2;
    protected final static int GROUP_SINGLE = 3;

    // 依附边为 左右边（vertical） 或是 上下边（horizontal）
    private int directionMode = VERTICAL;

    // 是否可多选
    private int selectMode = SINGLE;

    /**
     * 视图尺寸信息 map， groupId - 信息
     */
    protected HashMap<Integer, GroupViewData> viewDataHashMap;

    // 当前 popupWindow 到 anchor 的距离
    // 因为有了阴影的 8dp，所以这里不需要了，暂留
    private int marginToAnchor = 0;

    // 最后一次显示的位置
    private int lastShowDirection = 0;

    // 背景 padding 1234 对应 左上右下
    private int[] contentPadding;

    // 是否需要显示阴影
    private boolean showShadow = true;

    private ResSet[] resSets;
    protected Context context;
    private View[] items;
    private Map<String,View> viewDots = new HashMap<>();
    private int[] lastChecked;
    protected BaseAutoArrangePopupWindow mChild = null;
    protected BaseAutoArrangePopupWindow mParent = null;
    protected RelativeLayout shadowContainer = null;

    private Lifecycle lifecycle;

    /**
     * 视图尺寸信息 数据结构
     */
    protected static class GroupViewData {
        int lineWidth;
        int lineHeight;
        int itemWidth;
        int itemHeight;
        int maxNumInPerLine;

        /**
         * 构造方法
         *
         * @param context         上下文
         * @param lineWidth       行宽 - dp值
         * @param lineHeight      行高 - dp值
         * @param itemWidth       item单元宽度 - dp值
         * @param itemHeight      item单元高度 - dp值
         * @param maxNumInPerLine 每行最多个数
         */
        public GroupViewData(Context context, int lineWidth, int lineHeight, int itemWidth
                , int itemHeight, int maxNumInPerLine) {
            this.lineWidth = DisplayUtils.dip2px(context, lineWidth);
            this.lineHeight = DisplayUtils.dip2px(context, lineHeight);
            this.itemWidth = DisplayUtils.dip2px(context, itemWidth);
            this.itemHeight = DisplayUtils.dip2px(context, itemHeight);
            this.maxNumInPerLine = maxNumInPerLine;
        }
    }

    // item 资源集
    public static class ResSet {
        static final int DISPLAY_IMG_MODE = 1;
        static final int DISPLAY_TEXT_MODE = 2;
        public static final int DEFAULT_GROUP_ID = -1;

        // drawableId 默认显示状态图
        Drawable drawableNormal;

        // drawableIdChecked 点击后显示状态，为 null 则没有选中状态效果
        Drawable drawableChecked;

        Drawable drawableDot;

        // descriptionText
        // DISPLAY_IMG_MODE -- imageView 下的描述文字
        // DISPLAY_TEXT_MODE -- 主显示区文字
        String descriptionText;

        // 文字颜色
        int textColor;

        // onClickListener item 点击监听，只需要处理popupWindow外的逻辑
        View.OnClickListener onClickListener;

        // 显示模式
        int displayMode = DISPLAY_IMG_MODE;

        // isEnable 是否启用，为false则不会显示这个item
        public boolean isEnable = true;
        //默认不显示右上角角标
        public boolean isShowDot = false;

        // selectMode 为 多选时，checkValue 值 ：0 为 未选，1为 选中
        // selectMode 为 分组单选时，checkValue 表示组 id
        int checkValue;

        /**
         * 初始化一个图片为主体的
         *
         * @param drawable        普通状态
         * @param descriptionText 描述文字，null或空，则不显示
         * @param onClickListener 点击事件
         */
        public ResSet(Drawable drawable, String descriptionText, View.OnClickListener onClickListener) {
            this(drawable, null,null, DEFAULT_GROUP_ID, DISPLAY_IMG_MODE, descriptionText
                    , 0, onClickListener);
        }

        /**
         * 初始化一个图片为主体的
         *
         * @param drawable        普通状态
         * @param descriptionText 描述文字，null或空，则不显示
         * @param drawableDot  右上角角标
         * @param onClickListener 点击事件
         */
        public ResSet(Drawable drawable, String descriptionText,Drawable drawableDot, View.OnClickListener onClickListener) {
            this(drawable, null,drawableDot, DEFAULT_GROUP_ID, DISPLAY_IMG_MODE, descriptionText
                    , 0, onClickListener);
        }

        /**
         * 初始化一个图片为主体的，有选中样式的，分组单选
         *
         * @param drawable        普通状态
         * @param drawableChecked 选中状态
         * @param groupId         组 ID
         * @param descriptionText 描述文字，null或空，则不显示
         * @param onClickListener 点击事件
         */
        public ResSet(Drawable drawable, Drawable drawableChecked, int groupId,
                      String descriptionText, View.OnClickListener onClickListener) {
            this(drawable, drawableChecked,null, groupId, DISPLAY_IMG_MODE, descriptionText
                    , 0, onClickListener);
        }

        /**
         * 初始化一个文字为主体的
         *
         * @param descriptionText 显示的文字
         * @param textColor       文字颜色
         * @param onClickListener 点击事件
         */
        public ResSet(String descriptionText, int textColor, View.OnClickListener onClickListener) {
            this(null, null,null, DEFAULT_GROUP_ID, DISPLAY_TEXT_MODE,
                    descriptionText, textColor, onClickListener);
        }

        private ResSet(Drawable drawable, Drawable drawableChecked,Drawable drawableDot, int groupId,
                       int displayMode, String descriptionText, int textColor,
                       View.OnClickListener onClickListener) {
            this.drawableNormal = drawable;
            this.drawableChecked = drawableChecked;
            this.drawableDot = drawableDot;
            this.displayMode = displayMode;
            this.checkValue = groupId;
            this.descriptionText = descriptionText;
            this.textColor = textColor;
            this.onClickListener = onClickListener;
        }
    }

    public BaseAutoArrangePopupWindow(Context context) {
        super(context);
        this.context = context;
        // 初始化部分默认值
        viewDataHashMap = new HashMap<>();
        viewDataHashMap.put(ResSet.DEFAULT_GROUP_ID,
                new GroupViewData(context, 166, 66, 50, 50, 3));
        contentPadding = new int[4];
        Arrays.fill(contentPadding, 0);

        if (context instanceof AppCompatActivity) {
            lifecycle = ((AppCompatActivity) context).getLifecycle();
            lifecycle.addObserver(this);
        }
    }

    private boolean isDefaultView = true;

    protected void createView() {
        if (resSets == null || resSets.length == 0) {
            return;
        }
        if (selectMode == SINGLE || lastChecked == null) {
            lastChecked = new int[]{-1};
        }
        initViewDataHashMap();
        ScrollView scrollView = new ScrollView(context);
        LinearLayout contentView = new LinearLayout(context);
        int maxLineWidth = 0;
        for (GroupViewData viewData :
                viewDataHashMap.values()) {
            maxLineWidth = Math.max(viewData.lineWidth, maxLineWidth);
        }
        ConstraintLayout.LayoutParams contentParams = new ConstraintLayout.LayoutParams(
                maxLineWidth + contentPadding[0] + contentPadding[2], ViewGroup.LayoutParams.WRAP_CONTENT);
        contentView.setOrientation(LinearLayout.VERTICAL);
        contentView.setLayoutParams(contentParams);
        GradientDrawable drawable = new GradientDrawable();
        drawable.setColor(ThemeDataUtil.getColorFromThemeConfigByAttrId(context, R.attr.base_theme_brand_container_color));
        drawable.setCornerRadius(context.getResources().getDimensionPixelSize(R.dimen.bjy_base_common_bg_radius));
        contentView.setBackground(drawable);
        contentView.setPadding(contentPadding[0], contentPadding[1], contentPadding[2], contentPadding[3]);
        scrollView.addView(contentView);
        createView(scrollView, true);
    }

    protected void createView(View contentView, boolean isDefaultView) {
        if (showShadow) {
            ConstraintLayout rootView = new ConstraintLayout(context);
            ConstraintLayout.LayoutParams rootParams = new ConstraintLayout.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            rootView.setLayoutParams(rootParams);
            if (contentView.getId() == View.NO_ID) {
                contentView.setId(View.generateViewId());
            }
            shadowContainer = new RelativeLayout(context);

            rootView.setPadding(DisplayUtils.dip2px(context, 8),
                    DisplayUtils.dip2px(context, 8),
                    DisplayUtils.dip2px(context, 8),
                    DisplayUtils.dip2px(context, 8));
            rootView.addView(contentView);
            rootView.addView(shadowContainer);

            ConstraintLayout.LayoutParams shadowParams = new ConstraintLayout.LayoutParams(0, 0);
            shadowParams.startToStart = contentView.getId();
            shadowParams.endToEnd = contentView.getId();
            shadowParams.topToTop = contentView.getId();
            shadowParams.bottomToBottom = contentView.getId();
            shadowContainer.setLayoutParams(shadowParams);

            ShadowUtil.setViewBoundShadow(shadowContainer);
            setContentView(rootView);
        } else {
            setContentView(contentView);
        }
        
        setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
        setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
        setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        setFocusable(false);
        setOutsideTouchable(true);
        setTouchable(true);

        if (isDefaultView) {
            generateView((LinearLayout) ((ViewGroup)contentView).getChildAt(0));
        }

        this.isDefaultView = isDefaultView;
        initTouchListener();
    }

    protected void generateView(LinearLayout contentView) {
        ConstraintLayout nowLine = null;
        int lastGroupId = resSets[0].checkValue;
        // 当前行个数已有 item 个数

        int nowLineMaxNum = getNowItemData(0).maxNumInPerLine;

        for (int i = 0; i < resSets.length; i++) {
            if (!resSets[i].isEnable) {
                continue;
            }
            if (nowLine == null || nowLine.getChildCount() % nowLineMaxNum == 0
                    || (lastGroupId != resSets[i].checkValue && nowLine.getChildCount() != 0)) {
                // 行满 or 换组 前 补充占位
                generateHolders(nowLine, i - 1);
                if (nowLine != null) {
                    contentView.addView(nowLine);
                }
                if (lastGroupId != resSets[i].checkValue) {
                    // 换组 加入分割线
                    View divideLine = new View(context);
                    LinearLayout.LayoutParams divideParams = new LinearLayout.LayoutParams(
                            ViewGroup.LayoutParams.MATCH_PARENT, DisplayUtils.dip2px(context, 1));
                    divideParams.setMarginStart(DisplayUtils.dip2px(context, 10));
                    divideParams.setMarginEnd(DisplayUtils.dip2px(context, 10));
                    divideLine.setLayoutParams(divideParams);
                    divideLine.setBackgroundColor(getColor(R.color.base_divider_line_other));
                    contentView.addView(divideLine);

                    // 更新 group ID 、 maxNumInPerLine
                    lastGroupId = resSets[i].checkValue;
                    nowLineMaxNum = getNowItemData(i).maxNumInPerLine;
                }
                nowLine = new ConstraintLayout(context);
                LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                        getNowItemData(i).lineWidth, getNowItemData(i).lineHeight);
                params.gravity = Gravity.CENTER_HORIZONTAL;
                nowLine.setLayoutParams(params);
            }

            View tmpGenView = generateLineItem(nowLine, i, false);
            if (!(tmpGenView instanceof ConstraintLayout)) {
                continue;
            }
            ConstraintLayout item = (ConstraintLayout) tmpGenView;
            int itemSize = getNowItemData(i).itemWidth;
            final ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(itemSize, itemSize);
            layoutParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
            layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
            layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
            if (resSets[i].displayMode == ResSet.DISPLAY_IMG_MODE) {
                ImageView imageView = new ImageView(context);
                imageView.setLayoutParams(layoutParams);
                imageView.setImageDrawable(resSets[i].drawableNormal);
                imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
                item.addView(imageView);
                imageView.setId(View.generateViewId());
                items[i] = imageView;
            } else {
                TextView textView = new TextView(context);
                textView.setLayoutParams(layoutParams);
                textView.setGravity(Gravity.CENTER);
                textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
                textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getNowItemData(i).itemWidth >> 1);
                textView.setTypeface(Typeface.DEFAULT_BOLD);
                textView.setText(resSets[i].descriptionText);
                textView.setTextColor(resSets[i].textColor);
                item.addView(textView);
                textView.setId(View.generateViewId());
                items[i] = textView;
            }
            //endregion
            //右上角角标
            if (resSets[i].drawableDot != null) {
                View view = new View(context);
                view.setBackground(resSets[i].drawableDot);
                int size = DisplayUtils.dip2px(context, 8);
                ConstraintLayout.LayoutParams dotParams = new ConstraintLayout.LayoutParams(size,size);
                dotParams.circleConstraint = items[i].getId();
                dotParams.circleAngle = 45;
                dotParams.circleRadius = itemSize / 2;
                item.addView(view,dotParams);
                view.setVisibility(resSets[i].isShowDot ? View.VISIBLE : View.GONE);
                viewDots.put(resSets[i].descriptionText, view);
            }
            //region description 不空，则为 item 增加底部文字
            if (!TextUtils.isEmpty(resSets[i].descriptionText) && resSets[i].displayMode == ResSet.DISPLAY_IMG_MODE) {
                TextView textView = new TextView(context);
                ConstraintLayout.LayoutParams tvParams = new ConstraintLayout.LayoutParams(
                        ConstraintLayout.LayoutParams.WRAP_CONTENT, ConstraintLayout.LayoutParams.WRAP_CONTENT);
                tvParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
                tvParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
                tvParams.topToBottom = items[i].getId();
                tvParams.topMargin = DisplayUtils.dip2px(context, 2);
                textView.setLayoutParams(tvParams);
                textView.setText(resSets[i].descriptionText);
                textView.setTextColor(ThemeDataUtil.getColorFromThemeConfigByAttrId(context, R.attr.base_theme_bottom_menu_text_color));
                textView.setTextSize(12);
                textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
                textView.setSingleLine(true);
                textView.setEllipsize(TextUtils.TruncateAt.END);
                item.addView(textView);
            }
            //endregion

            //region 点击事件 及 UI 变更
            int finalI = i;
            item.setOnClickListener(v -> updateUIOfIndex(finalI, v));
            //endregion
        }
        if (nowLine != null) {
            generateHolders(nowLine, resSets.length - 1);
            contentView.addView(nowLine);
        }
    }

    public void setDotsVisibility(String key,boolean visible) {
        final View view = viewDots.get(key);
        if (view != null) {
            view.setVisibility(visible ? View.VISIBLE : View.GONE);
        }
    }

    private void generateHolders(ConstraintLayout nowLine, int nowResIndex) {
        if (checkIndexValueInvalid(nowResIndex) || nowLine == null) {
            return;
        }
        int maxNum = getNowItemData(nowResIndex).maxNumInPerLine;
        for (int i = nowLine.getChildCount(); i < maxNum; ) {
            View holderView = generateLineItem(nowLine, nowResIndex, true);
            if (holderView != null) {
                holderView.setVisibility(View.INVISIBLE);
                i = nowLine.getChildCount();
            }
        }
    }

    private View generateLineItem(ConstraintLayout nowLine, int nowResIndex, boolean generateHolder) {
        if (nowLine == null || context == null) {
            return null;
        }

        View nowLineLastView = nowLine.getChildAt(nowLine.getChildCount() - 1);
        ConstraintLayout.LayoutParams itemParams = new ConstraintLayout.LayoutParams(
                getNowItemData(nowResIndex).itemWidth, getNowItemData(nowResIndex).itemHeight);
        itemParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
        itemParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;

        if (nowLineLastView == null) {
            itemParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
        } else {
            itemParams.startToEnd = nowLineLastView.getId();
        }
        itemParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;

        View view;
        if (generateHolder) {
            view = new View(context);
        } else {
            view = new ConstraintLayout(context);
        }
        view.setLayoutParams(itemParams);
        view.setId(View.generateViewId());
        if (nowLineLastView != null) {
            ConstraintLayout.LayoutParams lastItemParams = (ConstraintLayout.LayoutParams) nowLineLastView.getLayoutParams();
            lastItemParams.endToEnd = ConstraintLayout.LayoutParams.UNSET;
            lastItemParams.endToStart = view.getId();
            nowLineLastView.setLayoutParams(lastItemParams);
        }
        nowLine.addView(view);
        return view;
    }

    @SuppressLint("ClickableViewAccessibility")
    private void initTouchListener() {
        this.setTouchInterceptor((v, event) -> {
            if (event.getAction() == MotionEvent.ACTION_OUTSIDE && mParent == null) {
                dismiss();
            }
            return false;
        });
    }

    private GroupViewData getNowItemData(int index) {
        return viewDataHashMap.get(resSets[index].checkValue);
    }

    private void updateUIOfIndex(int index, View view) {
        updateUIOfIndex(index, view, true);
    }

    /**
     * 刷新UI
     */
    private void updateUIOfIndex(int index, View view, boolean callClick) {
        if (checkIndexValueInvalid(index)) {
            return;
        }

        if (resSets[index].displayMode == ResSet.DISPLAY_IMG_MODE) {
            ImageView imageView = ((ImageView) items[index]);
            if (selectMode == SINGLE) {
                if (lastChecked[0] > -1) {
                    ((ImageView) items[lastChecked[0]]).setImageDrawable(resSets[lastChecked[0]].drawableNormal);
                    items[lastChecked[0]].setSelected(false);
                }
                if (resSets[index].drawableChecked != null) {
                    imageView.setImageDrawable(resSets[index].drawableChecked);
                }
                imageView.setSelected(true);
                lastChecked[0] = index;
            } else if (selectMode == MULTIPLE) {
                if (resSets[index].checkValue != 1) {
                    imageView.setImageDrawable(resSets[index].drawableChecked);
                    resSets[index].checkValue = 1;
                } else {
                    imageView.setImageDrawable(resSets[index].drawableNormal);
                    resSets[index].checkValue = 0;
                }
            } else if (selectMode == GROUP_SINGLE) {
                for (int groupInd = 0; groupInd < lastChecked.length; groupInd++) {
                    int lastCheckedInd = lastChecked[groupInd];
                    if (lastCheckedInd == -1) {
                        lastChecked[groupInd] = index;
                        break;
                    } else if (resSets[lastCheckedInd].checkValue == resSets[index].checkValue) {
                        ((ImageView) items[lastCheckedInd]).setImageDrawable(resSets[lastCheckedInd].drawableNormal);
                        lastChecked[groupInd] = index;
                        break;
                    }
                }
                if (resSets[index].drawableChecked != null) {
                    imageView.setImageDrawable(resSets[index].drawableChecked);
                }
            }
        }
        if (callClick) {
            resSets[index].onClickListener.onClick(view);
        }
    }

    /**
     * 设置 content View padding
     */
    protected void setContentPadding(int left, int top, int right, int bottom) {
        if (contentPadding == null) {
            contentPadding = new int[4];
        }
        contentPadding[0] = left;
        contentPadding[1] = top;
        contentPadding[2] = right;
        contentPadding[3] = bottom;
    }

    /**
     * 为resSets赋值
     *
     * @param resSets 资源集
     */
    protected void setResSets(ResSet[] resSets) {
        if (resSets == null) {
            return;
        }
        items = new View[resSets.length];
        this.resSets = resSets;

        List<Integer> groupList = new ArrayList<>();
        for (ResSet resSet : resSets) {
            if (!groupList.contains(resSet.checkValue)) {
                groupList.add(resSet.checkValue);
            }
        }
        lastChecked = new int[groupList.size()];
        Arrays.fill(lastChecked, -1);
    }

    public void setShowShadow(boolean showShadow) {
        this.showShadow = showShadow;
    }

    /**
     * 初始化颜色选中
     * @return 有颜色集或可以接收监听时 返回true
     * {@Warning 子类中请在此方法返回true时，再继续其余的操作}
     */
    @CallSuper
    protected boolean initColorsSelect() {
        if (!hasColors) {
            return false;
        }
        try {
            // 默认选中 第四个
            handleColorAllChangeRst(colorIds[4]);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 外部变更请调这个方法同步颜色
     * @param newColor String - "#FFFFFF" or ColorInt
     */
    protected void handleColorAllChangeRst(Object newColor) {
        int defaultSelect = 4;
        int defaultColor = colorIds[defaultSelect];
        if (newColor instanceof String) {
            if (!TextUtils.isEmpty((String) newColor)) {
                defaultColor = Color.parseColor((String) newColor);
            }
        } else if (newColor instanceof Integer) {
            defaultColor = (int) newColor;
        } else {
            // 不满足数据要求
            return;
        }

        for (int i = 0; i < colorIds.length; i++) {
            if (defaultColor == getColor(colorIds[i])) {
                defaultSelect = i;
                break;
            }
        }
        updateCheckedIndex(resSets.length - colorIds.length + defaultSelect);
    }

    public interface IOnColorSelectListener {
        void onColorSelect(int color);
    }

    private boolean hasColors = false;

    /**
     * 为resSets赋值，同时合并加入 color ResSet
     *
     * @param colorsGroupId         color ResSet 的groupId
     * @param onColorSelectListener 颜色选中监听
     */
    protected void setResSetsMergeColors(List<ResSet> resSets, int colorsGroupId
            , IOnColorSelectListener onColorSelectListener) {
        hasColors = true;
        ResSet[] colors = getColorResSets(colorsGroupId, onColorSelectListener);

        ResSet[] lastResSets = new ResSet[(resSets == null ? 0 : resSets.size()) + colors.length];
        for (int i = 0; i < lastResSets.length; i++) {
            if (i < (resSets == null ? 0 : resSets.size())) {
                lastResSets[i] = resSets.get(i);
            } else {
                lastResSets[i] = colors[i - (resSets == null ? 0 : resSets.size())];
            }
        }
        setResSets(lastResSets);
    }

    public static final int[] colorIds = new int[]{
            R.color.base_toolbar_orange_1,
            R.color.base_toolbar_orange_2,
            R.color.base_toolbar_yellow,
            R.color.base_toolbar_red,
            R.color.base_toolbar_blue,
            R.color.base_toolbar_blue_2,
            R.color.base_toolbar_blue_3,
            R.color.base_toolbar_pink,
            R.color.base_toolbar_brown,
            R.color.base_toolbar_green_1,
            R.color.base_toolbar_green_2,
            R.color.base_toolbar_purple,
            R.color.base_toolbar_white,
            R.color.base_toolbar_gray_1,
            R.color.base_toolbar_gray_2,
            R.color.base_toolbar_black,
    };

    /**
     * 获取 color resSet
     *
     * @param colorsGroupId         color ResSet 的groupId
     * @param onColorSelectListener 颜色选中监听
     */
    private ResSet[] getColorResSets(int colorsGroupId, IOnColorSelectListener onColorSelectListener) {
        ResSet[] colors = new ResSet[colorIds.length];
        int strokeColor = Color.WHITE;

        for (int i = 0; i < colors.length; i++) {
            LayerDrawable drawable = getDrawable(getColor(colorIds[i]), getColor(colorIds[i]));
            LayerDrawable drawableChecked = getDrawable(getColor(colorIds[i]), strokeColor);
            final int finalI = i;
            colors[i] = new ResSet(drawable, drawableChecked, colorsGroupId, "",
                    (v -> onColorSelectListener.onColorSelect(getColor(colorIds[finalI]))));
        }
        return colors;
    }

    private LayerDrawable getDrawable(int color, int strokeColor) {
        GradientDrawable drawable1 = new GradientDrawable();
        drawable1.setColor(color);
        drawable1.setStroke(DisplayUtils.dip2px(context, 2), Color.TRANSPARENT);
        drawable1.setCornerRadius(context.getResources().getDimensionPixelSize(R.dimen.bjy_base_common_bg_radius));

        GradientDrawable drawable2 = new GradientDrawable();
        drawable2.setColor(Color.TRANSPARENT);
        drawable2.setStroke(DisplayUtils.dip2px(context, 2), strokeColor);
        drawable2.setCornerRadius(context.getResources().getDimensionPixelSize(R.dimen.bjy_base_common_bg_radius) >> 1);

        return new LayerDrawable(new Drawable[]{drawable1, drawable2});
    }

    /**
     * 初始化 视图尺寸等信息
     * 使用默认 视图 生成方法 @Link  createView() 时，
     */
    protected abstract void initViewDataHashMap();

    /**
     * 设置选择模式 默认为 single 单选
     *
     * @param selectMode SINGLE / MULTIPLE / GROUP_SINGLE
     */
    protected void setSelectMode(int selectMode) {
        this.selectMode = selectMode;
    }

    /**
     * 更新 textView
     *
     * @param index     index
     * @param text      更新的文字，null 则不更新
     * @param textColor 更新的颜色，0 则不更新
     */
    protected void updateTextView(int index, String text, int textColor) {
        if (checkIndexValueInvalid(index)) {
            return;
        }

        if (!(items[index] instanceof TextView)) {
            return;
        }

        TextView textView = (TextView) items[index];
        if (text != null) {
            textView.setText(text);
        }
        if (textColor != 0) {
            textView.setTextColor(textColor);
        }
    }

    protected void updateCheckedIndex(int index) {
        if (checkIndexValueInvalid(index)) {
            return;
        }
        if (items[index].getParent() instanceof View) {
            updateUIOfIndex(index, (View) items[index].getParent(), false);
        }
    }

    public void checkedIndex(int index) {
        if (checkIndexValueInvalid(index)) {
            return;
        }
        if (items[index].getParent() instanceof View) {
            updateUIOfIndex(index, (View) items[index].getParent(), true);
        }
    }

    public void checkedColorIndex(int index) {
        if (checkIndexValueInvalid(index)) {
            return;
        }
        if (items[index].getParent() instanceof View) {
            updateUIOfIndex(index, (View) items[index].getParent(), true);
            isFirstShow = false;
        }
    }

    protected Drawable getDrawable(int drawableId) {
        return getDrawable(drawableId, false);
    }

    /**
     * 获取有 底色阴影的盖层
     *
     * @param drawableId        drawableId
     * @param mergeFloatLayerBg true 则合并浮层背景
     */
    protected Drawable getDrawable(int drawableId, boolean mergeFloatLayerBg) {
        if (!mergeFloatLayerBg) {
            return AppCompatResources.getDrawable(context, drawableId);
        } else {
            return new LayerDrawable(new Drawable[]{
                    AppCompatResources.getDrawable(context, R.drawable.base_tool_bar_second_check_layer_bg),
                    AppCompatResources.getDrawable(context, drawableId)
            });
        }
    }

    protected int getColor(int colorId) {
        int color = Color.TRANSPARENT;
        try {
            color = ContextCompat.getColor(context, colorId);
        }catch (Resources.NotFoundException e) {
            e.printStackTrace();
        }
        return color;
    }

    protected void setDirectionMode(int directionMode) {
        this.directionMode = directionMode;
    }

    private int autoMeasureDefaultDirection = 0;

    protected void showAsAuto(View anchor) {
        if (autoMeasureDefaultDirection == 0) {
            int[] anchorDirection = getAnchor2limit(anchor);

            // 左半侧显示右边，右半侧显示在左边
            if (anchorDirection[0] + anchorDirection[1] < DisplayUtils.getScreenWidthPixels(context)) {
                autoMeasureDefaultDirection = LEFT_TOP;
            } else {
                autoMeasureDefaultDirection = RIGHT_TOP;
            }

            // 上半侧显示下边
            if (anchorDirection[2] + anchorDirection[2] < DisplayUtils.getScreenHeightPixels((Activity) context)) {
                autoMeasureDefaultDirection *= -1;
            }
        }
        showWithViewOfDirection(anchor, autoMeasureDefaultDirection);
    }

    public abstract void show(View anchor);

    /**
     * 当前popupWindow有子弹窗时，调用此方法设置
     *
     * @param popupWindow 子弹窗
     */
    public void setChild(BaseAutoArrangePopupWindow popupWindow) {
        if (popupWindow != null) {
            this.mChild = popupWindow;
            popupWindow.setParent(this);
        }
    }

    private void setParent(BaseAutoArrangePopupWindow popupWindow) {
        this.mParent = popupWindow;
    }

    boolean isFirstShow = true;

    /**
     * 使当前 popupWindow 显示在 {@param anchor} 的 {@param direction} 方向
     */
    protected void showWithViewOfDirection(View anchor, int direction) {
        if (System.currentTimeMillis() - lastDismissTime < 200) {
            // 200毫秒内不允许重复显示
            return;
        }

        if (isDefaultView && resSets.length == 0) {
            return;
        }

        if (isFirstShow) {
            initColorsSelect();
            isFirstShow = false;
        }

        //region 检查direction
        if (mParent != null && mParent.getLastShowDirection() != 0) {
            // 子弹窗使用父弹窗的方向
            direction = mParent.getLastShowDirection();
            this.rootAnchor = mParent.rootAnchor;
        } else if (direction == CENTER_ZERO) {
            this.rootAnchor = anchor;
        } else {
            this.rootAnchor = anchor;
            int[] anchor2limit = getAnchor2limit(anchor);

            int[][] requestSize = getRequestSize(anchor);

            // 如果需要在左边，但是左边放不下，则改为右侧
            if (Math.abs(direction) == 1 && anchor2limit[0] < (directionMode == VERTICAL
                    ? requestSize[0][0] : requestSize[1][0])) {
                direction *= 2;
            }

            // 如果需要在右边，但是右边放不下，则改为左侧
            if (Math.abs(direction) == 2 && anchor2limit[1] < (directionMode == VERTICAL
                    ? requestSize[0][0] : requestSize[1][0])) {
                direction /= 2;
            }

            // 上下不够时，取反
            // 两个方法不能合并，需要保证 向上 偏移的优先级
            if (direction > 0 && anchor2limit[2] < (directionMode == VERTICAL
                    ? requestSize[0][1] : requestSize[1][1])) {
                direction *= -1;
            }
            if (direction < 0 && anchor2limit[3] < (directionMode == VERTICAL
                    ? requestSize[0][1] : requestSize[1][1])) {
                direction *= -1;
            }
        }
        //endregion

        //region 设置动画及显示
        switch (direction) {
            case LEFT_TOP: {
                setAnimationStyle(R.style.BJYPushInOutFromRightBottom);
                break;
            }
            case RIGHT_TOP: {
                setAnimationStyle(R.style.BJYPushInOutFromLeftBottom);
                break;
            }
            case LEFT_BOTTOM: {
                setAnimationStyle(R.style.BJYPushInOutFromRightTop);
                break;
            }
            case RIGHT_BOTTOM: {
                setAnimationStyle(R.style.BJYPushInOutFromLeftTop);
                break;
            }
            case CENTER_ZERO: {
                setAnimationStyle(R.style.BJYPushInOutFromTop);
                break;
            }
            default: {
                setAnimationStyle(R.style.BJYPushInOutFromRightBottom);
                break;
            }
        }

        lastShowDirection = direction;

        // 根据位置显示
        initMyOffset(anchor);
        int[] offset2RootAnchor = getOffsetToRootAnchor();
        showAsDropDown(rootAnchor,
                offset2RootAnchor[0] + (
                        offset2RootAnchor[0] >= 0 ? customOffsetX : -customOffsetX
                ),
                offset2RootAnchor[1] + (
                        offset2RootAnchor[1] >= 0 ? customOffsetY : -customOffsetY
                ));
        //endregion
    }

    private int customOffsetX = 0;
    private int customOffsetY = 0;

    /**
     * init custom offset values with dp value
     * 传<b>正数</b>离 anchor 更<b>远</b>一点
     * 传<b>负数</b>离 anchor 更<b>近</b>一点
     */
    public void initCustomOffset(int customOffsetX, int customOffsetY) {
        this.customOffsetX = DisplayUtils.dip2px(context, customOffsetX);
        this.customOffsetY = DisplayUtils.dip2px(context, customOffsetY);
    }

    /**
     * 获取 显示需要的宽高
     *
     * @return [0][0]:vertical 时宽度  [0][1]:vertical 时高度
     * [1][0]:horizontal 时宽度  [1][1]:horizontal 时高度
     */
    private int[][] getRequestSize(View anchor) {
        getContentView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);

        int[][] requestSize = new int[2][2];
        int[][] childRequestSize = mChild == null ? new int[2][2] : mChild.getRequestSize(getContentView());

        // vertical 吸附方式
        requestSize[0][0] = getContentView().getMeasuredWidth() + marginToAnchor + childRequestSize[0][0];
        requestSize[0][1] = Math.max(0, getContentView().getMeasuredHeight() - anchor.getMeasuredHeight());

        // horizontal 吸附方式
        requestSize[1][0] = Math.max(0, getContentView().getMeasuredWidth() - anchor.getMeasuredWidth());
        requestSize[1][1] = getContentView().getMeasuredHeight() + marginToAnchor + childRequestSize[1][1];

        return requestSize;
    }

    private int[] myOffset;
    private View rootAnchor;

    private WeakReference<View> limitView;

    /**
     * 为 popupWindow 设置限定区域，不传则默认为全屏
     *
     * @param view 限定区域
     */
    public void initLimitView(WeakReference<View> view) {
        limitView = view;
    }

    /**
     * 获取 anchor 到边界的距离
     *
     * @param anchor anchor
     * @return 0123 分别对应 左右上下
     */
    private int[] getAnchor2limit(View anchor) {
        int[] rst;

        int[] anchorDirection = new int[2];
        anchor.getLocationOnScreen(anchorDirection);

        rst = new int[]{
                anchorDirection[0],
                DisplayUtils.getScreenWidthPixels(context)
                        - anchorDirection[0] - anchor.getMeasuredWidth(),
                anchorDirection[1],
                DisplayUtils.getScreenHeightPixels(context)
                        - anchorDirection[1] - anchor.getMeasuredHeight()
        };
        if (limitView != null && limitView.get() != null) {
            int[] limitDirection = new int[2];
            limitView.get().getLocationOnScreen(limitDirection);
            rst[0] -= limitDirection[0];
            rst[1] -= DisplayUtils.getScreenWidthPixels(context)
                    - limitDirection[0] - limitView.get().getMeasuredWidth();
            rst[2] -= Math.max(limitDirection[1], context instanceof Activity
                    ? DisplayUtils.getStatusBarHeight((Activity) context) : 0);
            rst[3] -= context instanceof Activity
                    ? Math.max(DisplayUtils.getScreenHeightPixels(context)
                            - limitDirection[1] - limitView.get().getMeasuredHeight()
                    , DisplayUtils.getNavigationBarHeight((Activity) context)) : 0;
        }
        return rst;
    }


    private void initMyOffset(View anchor) {
        int[] anchor2limit = getAnchor2limit(anchor);

        int[][] requestSize = getRequestSize(anchor);

        myOffset = new int[2];
        int mWidth = getContentView().getMeasuredWidth();
        int mHeight = getContentView().getMeasuredHeight();
        myOffset[0] = (Math.abs(lastShowDirection) == 1)
                ? (directionMode == VERTICAL
                ? (-1 * (mWidth + marginToAnchor + getRealOffset(requestSize[0][0], anchor2limit[0])))
                : (-1 * (mWidth - anchor.getMeasuredWidth()) + getRealOffset(requestSize[1][0], anchor2limit[0])))
                : (directionMode == VERTICAL
                ? (anchor.getMeasuredWidth() + marginToAnchor + getRealOffset(requestSize[0][1], anchor2limit[1]))
                : getRealOffset(requestSize[1][1], anchor2limit[1]));
        myOffset[1] = (lastShowDirection > 0)
                ? (directionMode == VERTICAL
                ? (-1 * (mHeight + getRealOffset(requestSize[0][1], anchor2limit[2])))
                : (-1 * (mHeight + anchor.getMeasuredHeight() + marginToAnchor + getRealOffset(requestSize[1][1], anchor2limit[2]))))
                : (directionMode == VERTICAL
                ? (-1 * anchor.getMeasuredHeight() + getRealOffset(requestSize[0][1], anchor2limit[3]))
                : marginToAnchor + getRealOffset(requestSize[1][1], anchor2limit[3]));
    }

    private int getRealOffset(int requestSpace, int remainSpace) {
        return Math.min(0, remainSpace - requestSpace);
    }

    private int[] getOffsetToRootAnchor() {
        int[] mOffset;
        if (lastShowDirection == CENTER_ZERO) {
            mOffset = new int[2];
            rootAnchor.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
            mOffset[1] = -rootAnchor.getMeasuredHeight();
        } else {
            mOffset = this.myOffset;
            if (this.mParent != null) {
                mOffset[0] += mParent.myOffset[0];
                mOffset[1] += mParent.myOffset[1] + mParent.getContentView().getMeasuredHeight();
            }
        }
        return mOffset;
    }

    private int getLastShowDirection() {
        return lastShowDirection;
    }

    private boolean checkIndexValueInvalid(int index) {
        return items == null || index < 0 || index > items.length - 1;
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestroy() {
        if (lifecycle != null) {
            lifecycle.removeObserver(this);
        }
        lifecycle = null;
        context = null;
    }

    private long lastDismissTime = -1;

    @Override
    public void dismiss() {
        lastDismissTime = System.currentTimeMillis();
        // 家族连带 dismiss
        if (mChild != null) {
            mChild.dismiss();
        }
        super.dismiss();
    }
}
